package hasthi.actuators.impl;

import hasthi.actuators.RemoteHostProxy;
import hasthi.common.BaseContext;
import hasthi.common.HasthiException;
import hasthi.common.Utils;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.tools.ProxyRenewer;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.ftp.Buffer;
import org.globus.ftp.DataChannelAuthentication;
import org.globus.ftp.DataSink;
import org.globus.ftp.GridFTPClient;
import org.globus.ftp.HostPort;
import org.globus.ftp.Marker;
import org.globus.ftp.MarkerListener;
import org.globus.ftp.Session;
import org.globus.gram.Gram;
import org.globus.gram.GramAttributes;
import org.globus.gram.GramException;
import org.globus.gram.GramJob;
import org.globus.gram.internal.GRAMConstants;
import org.globus.gsi.gssapi.auth.HostAuthorization;
import org.globus.io.urlcopy.UrlCopy;
import org.globus.util.GlobusURL;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;


public class GridbasedHostProxy implements RemoteHostProxy {
    private static Log log = LogFactory.getLog(HasthiConstants.LOGGER_NAME);
    private ProxyRenewer proxyRenewer;
    private BaseContext baseContext;
    

    public GridbasedHostProxy(BaseContext context) throws HasthiException {
        this.baseContext = context;
        proxyRenewer = new ProxyRenewer(baseContext);
    }

    /* (non-Javadoc)
     * @see hasthi.tools.RemoteHostProxy#runCommand(java.lang.String, java.lang.String, java.lang.String[], java.lang.String, boolean, java.lang.String)
     */
    public String[] runCommand(String hostName, String command, String[] args, String workDir,
            boolean wait4Complete,String verificationString) throws HasthiException {
        try {
            GramAttributes jobAttr = new GramAttributes();

            String name = new File(command).getName().replace(".sh", "");
            Date startTime = new Date();
            String postfix = startTime.toString().replaceAll("([ :\t])+", "_");
            String stdout = workDir + "/" + name +"_" +postfix+".log";
            String stderr = stdout;
            jobAttr.setDirectory(workDir);
            jobAttr.setStdout(stdout);
            jobAttr.setStderr(stderr);

            jobAttr.setJobType(GramAttributes.JOBTYPE_SINGLE);
            jobAttr.setExecutable(command);
            if (args != null) {
                for (String arg : args) {
                    jobAttr.addArgument(arg);
                }
            }

            GramJob job = new GramJob(jobAttr.toRSL());
            job.setCredentials(proxyRenewer.getCredentail());
            Gram.request(hostName, job, false, false);

            while (true) {
                int jobStatus = job.getStatus();
                log.info("Status of job " + job.getID() + " Status  " + job.getStatusAsString());
                if (jobStatus == GRAMConstants.STATUS_DONE
                        || (!wait4Complete && jobStatus == GRAMConstants.STATUS_ACTIVE)) {
                    break;
                } else if (jobStatus == GRAMConstants.STATUS_FAILED) {
                    throw new HasthiException("Failed Command " + command + " on " + hostName
                            + " Error is " + job.getError(), FaultCode.InternalServiceError);
                }
                Thread.sleep(1000);
            }
            log.info(stdout+ ", " + stderr);
            
            findAndValidateOutput(hostName, stdout, verificationString);
            return null;
        } catch (GramException e) {
            throw new HasthiException(e, FaultCode.InternalServiceError);
        } catch (GSSException e) {
            throw new HasthiException(e, FaultCode.InternalServiceError);
        } catch (InterruptedException e) {
            throw new HasthiException(e, FaultCode.InternalServiceError);
        }
    }

 

    /* (non-Javadoc)
     * @see hasthi.tools.RemoteHostProxy#copy(java.net.URI, java.net.URI)
     */
    public URI copy(URI src, URI dest) throws HasthiException {
        log.info("Tarnsfer Started" + src + "->" + dest);
        try {
            String srchost = src.getHost();
            String srcfile = src.getPath();
            String desthost = dest.getHost();
            String destfile = dest.getPath();

            GridFTPClient ftpClient;

            if (isLocalHost(srchost) && isLocalHost(desthost)) {
                FileInputStream in = new FileInputStream(srcfile);
                FileOutputStream out = new FileOutputStream(destfile);
                byte[] buf = new byte[1024];
                int read;
                while ((read = in.read(buf)) > 0) {
                    out.write(buf, 0, read);
                }
                out.close();
                in.close();
            } else {
                GSSCredential credentail = proxyRenewer.getCredentail();
                if (srchost.equals(desthost)) {
                    UrlCopy uc = new UrlCopy();
                    uc.setSourceCredentials(credentail);
                    uc.setDestinationCredentials(credentail);
                    uc.setSourceAuthorization(new HostAuthorization("host"));
                    uc.setUseThirdPartyCopy(false);
                    uc.setAppendMode(false);
                    uc.setSourceUrl(new GlobusURL("gsiftp://" + srchost + srcfile));
                    uc.setDestinationUrl(new GlobusURL("gsiftp://" + desthost + destfile));
                    uc.copy();
                } else if (isLocalHost(srchost)) {
                    ftpClient = new GridFTPClient(desthost, 2811);
                    ftpClient.setAuthorization(new HostAuthorization("host"));
                    ftpClient.authenticate(credentail);
                    ftpClient.setDataChannelAuthentication(DataChannelAuthentication.SELF);
                    boolean append = false;
                    ftpClient.put(new File(srcfile), destfile, append);
                    ftpClient.close();
                } else if (isLocalHost(desthost)) {
                    UrlCopy uc = new UrlCopy();
                    uc.setSourceCredentials(credentail);
                    uc.setDestinationCredentials(credentail);
                    uc.setSourceAuthorization(new HostAuthorization("host"));
                    uc.setUseThirdPartyCopy(false);
                    uc.setAppendMode(false);
                    uc.setSourceUrl(new GlobusURL("gsiftp://" + srchost + srcfile));
                    uc.setDestinationUrl(new GlobusURL("gsiftp://" + desthost + destfile));
                    uc.copy();
                } else {
                    parameterizedCopy(srchost, desthost, srcfile, destfile, true);
                }
            }
            log.info("Transfer Completed" + srchost + "(" + srcfile + ")" + "->" + desthost + "("
                    + destfile + ")");
            return dest;
        } catch (Exception e) {
            throw new HasthiException(e, FaultCode.InternalServiceError);
        }
    }

    /* (non-Javadoc)
     * @see hasthi.tools.RemoteHostProxy#copyToDir(java.net.URI, java.net.URI)
     */
    public URI copyToDir(URI src, URI destDir) throws HasthiException {
        try {
            URI destFile = new URI(destDir.toString() + "/" + new File(src.getPath()).getName());
            return copy(src, destFile);
        } catch (URISyntaxException e) {
            throw new HasthiException(e, FaultCode.InvaliedLocalArgumnet);
        }
    }

    private void parameterizedCopy(String srchost, String desthost, String srcfile,
            String destfile, boolean srcActive) throws HasthiException {
        try {
            int retryCount = 0;
            GSSCredential credentail = proxyRenewer.getCredentail();
            while (retryCount < 3) {
                GridFTPClient destClient = new GridFTPClient(desthost, 2811);
                destClient.setAuthorization(new HostAuthorization("host"));
                destClient.authenticate(credentail);
                destClient.setType(Session.TYPE_IMAGE);

                GridFTPClient srcClient = new GridFTPClient(srchost, 2811);
                srcClient.setAuthorization(new HostAuthorization("host"));
                srcClient.authenticate(credentail);
                srcClient.setType(Session.TYPE_IMAGE);

                if (srcActive) {
                    HostPort hp = destClient.setPassive();
                    srcClient.setActive(hp);
                } else {
                    HostPort hp = srcClient.setPassive();
                    destClient.setActive(hp);
                }

                try {
                    /**
                     * Transfer a file. The transfer() function blocks until the
                     * transfer is complete.
                     */
                    srcClient.transfer(srcfile, destClient, destfile, false, null);
                    System.out.print("transfer done");
                } finally {
                    srcClient.close();
                    destClient.close();
                }
            }
        } catch (Exception e) {
            throw new HasthiException(e, FaultCode.InvaliedLocalArgumnet);
        }

    }

    public static boolean isLocalHost(String appHost) throws HasthiException {
        try {
            String localHost = InetAddress.getLocalHost().getCanonicalHostName();

            if (localHost.equals(appHost) || "localhost".equals(appHost)
                    || "127.0.0.1".equals(appHost)) {
                return true;
            } else {
                return false;
            }
        } catch (UnknownHostException e) {
            throw new HasthiException(e, FaultCode.InitalizationError);
        }
    }

    /* (non-Javadoc)
     * @see hasthi.tools.RemoteHostProxy#getBaseContext()
     */
    public BaseContext getBaseContext() {
        return baseContext;
    }
    
    protected void findAndValidateOutput(String host,String stdoutFile,String verificationStr) throws HasthiException {
        try {
            int loop = 0;
            Thread.sleep(2000);

            String stdout = null;
            while (loop < 5) {
                stdout = readFileFormServiceDir(host, stdoutFile);
                if (stdout.contains(verificationStr)) {
                    log.debug("Stdout retrived ="+stdout);
                    return;
                }
                Thread.sleep(2000);
                loop++;
            }
            throw new HasthiException("Failure  look at " + stdoutFile
                    + " for more information" + "\n[#stdout " + stdout
                    + "] \n",FaultCode.ErrorAtDependentService);
        } catch (InterruptedException e) {
            throw new HasthiException(e,FaultCode.LocalError);
        }
    }

    private String readFileFormServiceDir(String desthost, String stdoutFile) throws HasthiException {
        try {
            GridFTPClient ftpClient = new GridFTPClient(desthost, 2811);
            ftpClient.setAuthorization(new HostAuthorization("host"));
            ftpClient.authenticate(proxyRenewer.getCredentail());
            ftpClient.setDataChannelAuthentication(DataChannelAuthentication.SELF);
            boolean append = false;
            final ByteArrayOutputStream out = new ByteArrayOutputStream();
            ftpClient.extendedGet(stdoutFile, 1024*10, new DataSink() {
                public void write(Buffer buf) throws IOException {
                    out.write(buf.getBuffer());
                }
                public void close() throws IOException {
                    out.close();
                }
            
            }, new MarkerListener() {
                public void markerArrived(Marker arg0) {}
            });
            ftpClient.close();
            return new String(out.toByteArray());
            
//            File tempfile = File.createTempFile(System.currentTimeMillis()+""+Utils.randomInt(10000), "temp");
//            copy(new URI("gsiftp://"+host+"/"+stdoutFile), new URI("gsiftp://"+Utils.getLocalHost()+"/"+tempfile));
//            return Utils.readFile(tempfile.getAbsolutePath());
        } catch (IOException e) {
            throw new HasthiException(e,FaultCode.LocalError);
        } catch (Exception e) {
            throw new HasthiException(e,FaultCode.InvaliedLocalArgumnet);
        }
    }
    
}
